#include "raylib.h"
#include <ctime>
#include <vector>
using namespace std;

#define Row 20          //记录迷宫行数
#define Column 30       //记录迷宫列数
int Gap = 20;           //记录迷宫间隙宽度
Color color_B = RED;    //记录边界颜色
Color color_wall = BLUE;//记录迷宫内墙颜色

void ArrayCopy(int* cop, int* ori,int Length); //数组拷贝函数定义

/*定义一个方格的结构*/
struct Room
{
    int x, y;       //方格左上角坐标
    int isWall[4];  //0边界，1为墙，-1为无墙，用于记录四面墙状态，顺序为--上下左右
    bool isOpen;    //定义是否接通（主要用于生成迷宫时状态记录）

    //初始化必要参数
    void init(int x, int y, int* isWall, int isOpen){
        this->x = x, this->y =y;
        ArrayCopy(this->isWall, isWall, 4);
        this->isOpen = isOpen;
    }

    //绘画单个方格（画四面墙）
    void draw(int id_x, int id_y){
        //绘画时为了降低运算量，第一行绘制上、右、下
        //                    第一列绘制右、下、左
        //                    其他格绘制右、下

        //绘制上墙
        if(id_x==0){    //判断是否为第一行，仅有第一行要绘制上墙
            if(isWall[0]==1) DrawLine(x, y, x+Gap, y, color_wall);  //墙绘制
            else if(isWall[0]==0) DrawLine(x, y, x+Gap, y, color_B);//边界绘制        
        }
        //绘制右墙
        if(isWall[1]==1) DrawLine(x+Gap, y, x+Gap, y+Gap, color_wall);
        else if(isWall[1]==0) DrawLine(x+Gap, y, x+Gap, y+Gap, color_B);
        //绘制下墙
        if(isWall[2]==1) DrawLine(x+Gap, y+Gap, x, y+Gap, color_wall);
        else if(isWall[2]==0) DrawLine(x+Gap, y+Gap, x, y+Gap, color_B);
        //绘制左墙
        if(id_y==0){    //判断是否为第一列，仅有第一列要绘制左墙
            if(isWall[3]==1) DrawLine(x, y+Gap, x, y, color_wall);
            else if(isWall[3]==0) DrawLine(x, y+Gap, x, y, color_B);
        }
    }
};

Room room[Row][Column]; //定义迷宫方格数组
void GenMaze();         //定义迷宫生成函数

/*--------主函数-------*/
int main(void)
{
    //设置窗口参数      抗锯齿设置          去掉标题栏                  窗口可透明
    SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_WINDOW_UNDECORATED | FLAG_WINDOW_TRANSPARENT);
    InitWindow(800, 600, "迷宫 2D");   //初始化窗口
    SetTargetFPS(60);   //设置最大帧率

    //初始化各个方格
    int gapLeft = (GetScreenWidth()-Gap*Column)/2;      //得到迷宫到窗口左方间隙
    int gapUp = (GetScreenHeight()-Gap*Row)/2;          //得到迷宫到窗口上方间隙
    for(int i=0; i<Row; i++) for(int j=0; j<Column; j++){
        int isWall[]={1,1,1,1};
        if(i==0) isWall[0] = 0;             //如果为第一行，则上方墙设为边界
        else if(i==Row-1) isWall[2] = 0;    //如果为最后一行，则下方墙设为边界
        if(j==0) isWall[3] = 0;             //如果为第一列，则左方墙设置为边界
        else if(j==Column-1) isWall[1] = 0; //如果为最后一列，则右方墙设置为边界

        if(i==0 && j==0) isWall[3] = -1;    //如果为左上角的方格，设置其左边无墙（作为入口）
        else if(i==Row-1 && j==Column-1) isWall[1] = -1;//如果为右下角的方格，设置其右边无墙（作为出口）

        room[i][j].init(gapLeft+Gap*j, gapUp+Gap*i, isWall, false); //初始化方格
    }
    GenMaze(); //生成迷宫

    const char ps[] = "Move the window: Hold down the left mouse button and Move\nExit: ESC";
    while (!WindowShouldClose())
    {
        if(IsMouseButtonDown(MOUSE_BUTTON_LEFT)){ //检测左键是否按下
            SetWindowPosition(GetWindowPosition().x+GetMouseDelta().x, GetWindowPosition().y+GetMouseDelta().y);
        } //按住鼠标左键并移动可以移动窗口

        BeginDrawing();
            ClearBackground((Color){0,0,0,180});    //清理画布,透明度为180（0~255）

            DrawFPS(0, 0);  //绘制FPS
            DrawText(ps, 5, GetScreenHeight()-50, 20, LIGHTGRAY);   //绘制提示

            for(int i=0; i<Row; i++) for(int j=0; j<Column; j++){ 
                room[i][j].draw(i,j);  //调用结构体内实现的绘制函数，绘制方格
            }
            
        EndDrawing();
    }

    CloseWindow();

    return 0;
}

/*实现数组拷贝*/
//学指针时必学函数，直接写
void ArrayCopy(int* copy, int* root,int length)
{ 
  for(int i = 0; i < length; i++) 
  {
    *copy++ = *root++;
  } 
}

/*-------实现迷宫生成--------
方法：
    1.只能去没有去过的房间
    2.当出现四周房间都去过时，传送到一个去过的房间（这个房间周围有未去过房间）
    3.每个房间都要去过
ps：方法来自与网络(https://www.bilibili.com/video/BV1tK4y1W777), up主:暗流啊暗流
---------------------------*/
void GenMaze(){
    time_t now = time(0);	//获取时间戳
	SetRandomSeed(now);		//设置时间戳为随机数种子为时间戳

    vector<Vector2> id_noOpen;  //如果房间附近有多个没有去过的房间则记录该房间
    int id_x=0, id_y=0;
    room[0][0].isOpen = true;   //从左上角房间开始
    while (1)
    {   
        int open[]={0,0,0,0};
        int noOpenNum=0;    //记录可取方格数

        //检测上方格是否可去
        if (id_x-1>=0 && !room[id_x-1][id_y].isOpen){
            open[0]=1; 
            noOpenNum++;
        }
        //检测右方格是否可去
        if (id_y+1<=Column-1 && !room[id_x][id_y+1].isOpen){
            open[1]=1; 
            noOpenNum++;
        }
        //检测下方格是否可去
        if (id_x+1<=Row-1 && !room[id_x+1][id_y].isOpen){
            open[2]=1;
            noOpenNum++;
        }
        //检测左方格是否可去
        if (id_y-1>=0 && !room[id_x][id_y-1].isOpen){
            open[3]=1; 
            noOpenNum++;
        }

        //如果可去方格数不为0，随机去可去房间并开墙，如果没有可去房间则传送
        if(noOpenNum!=0){
            //如果可去房间为多个，则记录下这个房间用于以后传送   
            if(noOpenNum>=2) id_noOpen.push_back( (Vector2){id_x, id_y} );

            int random = GetRandomValue(1, noOpenNum);  //生成随机数
            int index=-1;   //记录随机打开的房间0-上，1-右，2-下，3-左
            while (random)
            {
                index++;
                if(open[index]==1) random--;
            }   //得到对应的房间号
            
            if(index==0) {      //进入上方格
                //设为进入，去掉之间的墙
                room[id_x-1][id_y].isOpen = true, room[id_x-1][id_y].isWall[2] = -1; 
                room[id_x][id_y].isWall[0] = -1;
                id_x--;
            }
            else if(index==1) {//进入右方格
                room[id_x][id_y+1].isOpen = true, room[id_x][id_y+1].isWall[3] = -1;
                room[id_x][id_y].isWall[1] = -1;
                id_y++;
            }
            else if(index==2) {//进入下方格
                room[id_x+1][id_y].isOpen = true, room[id_x+1][id_y].isWall[0] = -1;
                room[id_x][id_y].isWall[2] = -1;
                id_x++;
            }
            else if(index==3) {//进入左方格
                room[id_x][id_y-1].isOpen = true, room[id_x][id_y-1].isWall[1] = -1;
                room[id_x][id_y].isWall[3] = -1;
                id_y--;
            }
        }
        else{   //可去房间为0，则传送
            if(id_noOpen.size()==0) break;  //如果曾经记录为0，说明所有房间都去过了，跳出循环
            else {
                int random = GetRandomValue(0, id_noOpen.size()-1); //得到随机数，用于随机选取传的房间
                id_x = id_noOpen[random].x;
                id_y = id_noOpen[random].y;
                id_noOpen.erase(id_noOpen.begin()+random);  //去掉传送的房间的记录
            }  
        }
    }
}
